﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace LambdaManAi
{
    public delegate object UserFunction(object[] args);

    public class UserFunctions : LowLevelFunctions
    {
        //Args for the step
        private const int AiState = 0;
        private const int WorldState = 1;

        //4-tuple world
        private const int MapStatus = 0;
        private const int ManStatus = 1;
        private const int GhostSatatus = 2;
        private const int FruitSatus = 3;
        
        //Map cells
        private const int Wall = 0;
        private const int EmptySpace = 1;
        private const int Pill = 2;
        private const int PowerPill = 3;
        private const int Fruit = 4;
        private const int ManStartPosition = 5;
        private const int GhostStartPosition = 6;
        
        //Directions
        private const int MoveUp = 0;
        private const int MoveRight = 1;
        private const int MoveDown = 2;
        private const int MoveLeft = 3;

        //Man 5-tuple, ghost 3-tuple
        private const int Vitality = 0;
        private const int Location = 1;
        private const int Direction = 2;
        private const int Lives = 3;
        private const int Score = 4;

        //Ghost Vitality
        private const int Standard = 0;
        private const int Fright = 1;
        private const int Invisible = 2;

        //Cells price
        private const int PillPriсe = 10;        
        private const int PowerPillPriсe = 500;
        private const int FruitPriсe = 1000;


        #region Auxiliary code, don't modify it!

        public static IProcessor Processor { get; set; }

        private static object Code(Expression<Func<object>> expression, [CallerMemberName]string functionName = "")
        {
            return Processor.ProcessCode(functionName, expression);
        }

        private static object Code(Locals locals, Expression<Func<object>> expression, [CallerMemberName]string functionName = "")
        {
            return Processor.ProcessCode(functionName, locals, expression);
        }

        #endregion

        public static object MainFunction(params object[] args)
        {
            return Code(() =>
                Pair(1, Closure(StepFunction))
            );
        }

        public static object StepFunction(params object[] args)
        {
            // arguments
            const int stepState = 0;
            const int worldState = 1;

          return Code(() => Pair(1, NextCell(GetItemList(GetParam(args, worldState), MapStatus), 1, 3, 0)));
        }

        //Поиск в глубину пилюли, сначала вверх, потом вправо, потом вниз, потом влево
        public static object NextCell(params object[] args)
        {
            const int map = 0;
            const int y = 1;
            const int x = 2;
            const int distance = 3;
            //locals
            const int upcell = 4;
            const int rightcell = 6;
            const int downcell = 7;
            const int leftcell = 8;

            Locals locals = null;
            locals = new Locals(args)
            {
                {upcell, () =>GetCell(locals[map], (int)locals[y]-1, locals[x])},
            };

            return Code(locals, () =>
                    Select((int)locals[distance] > 40 || (int)GetCell(locals[map], locals[y], locals[x]) == Wall, -1,
                        Select((int)NextCell(locals[map], (int)locals[y] - 1, locals[x], (int)locals[distance] + 1) >= 0, MoveUp,
                        2
                    ))
             );
        }

        public static object GetCell(params object[] args)
        {
            const int map = 0;
            const int y = 1;
            const int x = 2;
            var locals = new Locals(args);
            return Code(() =>
                GetItemInt(GetItemList(locals[map], (int)locals[y]), (int)locals[x])
            );
        }
    }
}